/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.rultax.core;

import org.jbox2d.common.Transform;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.World;
import org.jbox2d.dynamics.joints.Joint;
import org.jbox2d.dynamics.joints.JointDef;
import org.jbox2d.dynamics.joints.RevoluteJoint;
import org.jbox2d.dynamics.joints.RevoluteJointDef;
import processing.core.PApplet;
import processing.core.PVector;

/**
 *
 * @author Scott
 */
public class Physics {
    private PApplet screen;
    private World world;
    
    private float transX;
    private float transY;
    private float scaleFactor;
    private float yFlip;
    
    public Physics(PApplet screen){
        this(screen, 10);
    }
    
    public Physics(PApplet screen, float scaleFactor){
        this.screen = screen;
        this.scaleFactor = scaleFactor;
        transX = screen.width / 2;
        transY = screen.height / 2;
        yFlip = -1;
    }
    
    public void step(){
        float timeStep = 1.0f / 60f;
        this.step(timeStep, 10, 10);
    }
    
    private void step(float dt, int velocityIterations, int positionIterations){
        world.step(dt, velocityIterations, positionIterations);
    }
    
    public void setWarmStarting(boolean b){
        world.setWarmStarting(b);
    }
    
    public void setContinuousPhysics(boolean b){
        world.setContinuousPhysics(b);
    }
    
    public void createWorld(){
        Vec2 gravity = new Vec2(0f, -9.8f);
        createWorld(gravity, true);
    }
    
    private void createWorld(Vec2 gravity, boolean doSleep){
        world = new World(gravity, doSleep);
        setWarmStarting(true);
        setContinuousPhysics(true);
    }
    
    public void setGravity(float x, float y){
        world.setGravity(new Vec2(x, y));
    }
    
    public PVector coordWorldToScreen(Vec2 coords){
        return coordWorldToScreen(coords.x, coords.y);
    }
    
    public PVector coordWorldToScreen(float x, float y){
        float screenX = PApplet.map(x, 0f, 1f, transX, transX + scaleFactor);
        float screenY = PApplet.map(y, 0f, 1f, transY, transY + scaleFactor);
        if (yFlip == -1) 
	    screenY = PApplet.map(screenY, 0f, screen.height, screen.height, 0f);
        return new PVector(screenX, screenY);
    }
    
    public Vec2 coordScreenToWorld(PVector coords){
        return coordScreenToWorld(coords.x, coords.y);
    }
    
    public Vec2 coordScreenToWorld(float x, float y){
        float worldX = PApplet.map(x, transX, transX + scaleFactor, 0, 1f);
        float worldY = y;
        if (yFlip == -1)
	    worldY = PApplet.map(y, screen.height, 0f, 0f, screen.height);
	worldY = PApplet.map(worldY, transY, transY + scaleFactor, 0f, 1f);
        return new Vec2(worldX, worldY);
    }
    
    public float scalarScreenToWorld(float val){
        return val / scaleFactor;
    }
    
    public float scalarWorldToScreen(float val){
        return val * scaleFactor;
    }
    
    public Vec2 vectorScreenToWorld(PVector vec){
        Vec2 u = new Vec2(vec.x / scaleFactor, vec.y / scaleFactor);
        u.y *= yFlip;
        return u;
    }
    
    public PVector vectorWorldToScreen(Vec2 v){
        PVector u = new PVector(v.x * scaleFactor, v.y * scaleFactor);
        u.y *= yFlip;
        return u;
    }
    
    public Body createBody(BodyDef bd){
        return world.createBody(bd);
    }
    
    public Joint createJoint(JointDef jd){
        return world.createJoint(jd);
    }
    
    public RevoluteJoint createRevoluteJoint(RevoluteJointDef jointDef){
	return (RevoluteJoint) world.createJoint(jointDef);
    }
    
    public PVector getBodyScreenCoord(Body b){
        Transform xf = b.getTransform();
        return coordWorldToScreen(xf.position);
        //return vectorWorldToScreen(xf.position);
    }
    
    public PVector getBodyScreenVector(Body b){
        Transform xf = b.getTransform();
        return coordWorldToScreen(xf.position);
    }
    
    public void destroyBody(Body b){
        world.destroyBody(b);
    }
}
